import sys
import threading
import time
from datetime import datetime

import sqlalchemy
from selenium import webdriver
import pandas as pd
from sqlalchemy import distinct
from sqlalchemy.orm import sessionmaker
from selenium.webdriver.chrome.options import Options
from models import Diamond, Price, Seller


def get_browser():
    """Get chrome instance"""
    chrome_options = Options()
    chrome_options.add_argument('--headless')
    driver = webdriver.Chrome(options=chrome_options)
    driver.maximize_window()
    # 下面加在setup最上面
    return driver


# chrome instance
document = get_browser()
NUMBER = "105877"
PASSWORD = "105877bo"
# request url
URL = "https://trade.rapnet.com"


class BasicAttrObject:
    """Return parameter"""
    # Class basic attributes
    BASE_ATTR = ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__',
                 '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__',
                 '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__',
                 '__sizeof__',
                 '__str__', '__subclasshook__', '__weakref__']

    ENGLISH = list('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')

    REP_STR = "_"

    MYSQL_CONN = sqlalchemy.create_engine('mysql+pymysql://root:123456@localhost/rapnet?charset=utf8')
    DBSession = sessionmaker(bind=MYSQL_CONN)
    session = DBSession()
    # Console color
    LOG_COLOR = {
        'R': 31,
        'G': 32,
        'B': 34
    }
    # is last operation
    EXIT_STATUS = False
    # Maximum number of waiting elements
    MAX_WAIT = 100
    # Whether to stop the test
    STOP_TEST = False
    # # table name
    # TABLE_NAME = 'req_data'
    # # 钻石的细节
    # DIAMOND_HEADER = [
    #     "id", "形状", "尺寸", "颜色", "净度", "切割", "打磨", "对称度", "荧光", "深度", "台面", "尺寸", "关键特征", "供应商评论", "底尖", "腰",
    #     "冠", "亭部", "处理的", "题词", "比率", "星型刻面长", "杂质", "色调", "牌子", "报告中的形状", "报告中的评论", "price_id", "seller_id"
    # ]
    # # 价格细节
    # PRICE_HEADER = ["id", "库存批号", "可用性", "钻石的地点", "库存号码", "每卡价格", "Rap百分比", "总数"]
    # # 卖家信息
    # SELLER_HEADER = ["id", "地点", "联系人", "电话", "RapNet账号", "RapNet地址"]


BasicAttr = BasicAttrObject()


def println(text, color: int = BasicAttr.LOG_COLOR['G']):
    """Instead of the standard library, use color printing"""
    line = sys._getframe().f_back.f_lineno
    prefix = "0;"
    center = ";"
    line_str = str(line).ljust(4)
    time_str = str(datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]).ljust(24)
    suffix = "{}m{}".format(color, f"LOGGER:[{line_str}] [{time_str}]    :{text}")
    out = "\033[{}{}{}\033[0m".format(prefix, center, suffix)
    print(out)


def error_detection():
    while 1:
        if BasicAttr.STOP_TEST:
            continue
        msg = None
        try:
            msg = document.find_element_by_id("main-message")
        except:
            println("Detect success")
        if msg or BasicAttr.EXIT_STATUS:
            println("Connection failed", BasicAttr.LOG_COLOR['R'])
            document.quit()
            exit(1)
        time.sleep(10)


def get_static_fields(top, end):
    """
    Get non-default global variables of the class
    """
    return list(set(top).difference(set(end)))


def wait_element(func, name, index=None, key=None):
    """Wait the operation of max wait`s count"""
    count = 0
    while 1:
        try:
            target = func(name)
            if index is not None:
                target = target[index]
            if key is not None:
                flag = key(target)
                if flag:
                    return target
        except Exception as e:
            time.sleep(1)
            println(str(e), color=BasicAttr.LOG_COLOR['R'])
        else:
            break
        count += 1
        if count == BasicAttr.MAX_WAIT:
            raise Exception("Exceeded the maximum number of acquisitions!!!!!!!")
    return target


def ignore(*args, **kwargs):
    pass


def get_footer():
    """footer obj,total,next"""
    footer = wait_element(document.find_element_by_id, "tableFooter-searchResultTable")
    triggerText = footer.find_element_by_class_name("triggerText")
    btns = footer.find_elements_by_tag_name("button")
    next_btn = btns[len(btns) - 1]
    disabled = next_btn.get_attribute("disabled")
    return footer, \
           int(triggerText.text), \
           next_btn if disabled is None else None


def parse_table(table_head, table_data):
    """Data is converted to multiple values"""
    data = []
    for item in table_data:
        sub = ['-' for _ in range(len(table_head) - len(item))]
        sub.extend(item)
        data.append({k: v for k, v in zip(table_head, sub)})

    return data


def setAttribute(driver, elementObj, attributeName, value):
    driver.execute_script("arguments[0].setAttribute(arguments[1],arguments[2])", elementObj, attributeName, value)


class ParseUtil:
    def __init__(self, diamond_element, price_element, seller_element):
        self.diamond_element = diamond_element
        self.price_element = price_element
        self.seller_element = seller_element
        self._price = None
        self._seller = None
        self._diamond = None

    def parse_price(self):
        data = {}
        header = {
            "库存批号 #": "stock_lot_number",
            "可用性": "availability",
            "钻石的地点": "location",
            "库存号码": "stock_number",
            #
            "每卡价格 $/Ct": "price_per_card",
            "Rap百分比": "rap_percentage",
            "$总数": "total",
        }
        table = self.price_element.find_element_by_class_name("table__expandSectionBoxContent--withPadding")
        price_table_first = None
        try:
            price_table_first = self.price_element.find_element_by_class_name("diamond-price__priceTableFirst") \
                .find_element_by_class_name("diamond-price__priceTable__row")
        except Exception as err:
            ignore(err)
        messy_div = table.find_elements_by_tag_name("div")

        for i in range(0, len(messy_div), 2):
            data[header[messy_div[i].text]] = messy_div[i + 1].text

        if not price_table_first:
            data['price_per_card'] = '-'
            data['rap_percentage'] = '-'
            data['total'] = '-'
        else:
            cash_div = price_table_first.find_elements_by_tag_name("div")

            data['price_per_card'] = cash_div[1].text
            data['rap_percentage'] = cash_div[2].text
            data['total'] = cash_div[3].text

        self._price = Price(**data)

    def parse_seller(self):
        data = {}
        header = {
            "地点": "address",
            "联系人": "availability",
            "电话": "phone_number",
        }

        table = self.seller_element.find_element_by_class_name("table__expandSectionBoxContent--withPadding")
        # seller info div
        info_div = table.find_element_by_class_name("account-details__accountSection"). \
            find_element_by_class_name("flexboxgrid2__col-xs-9")
        data['homepage'] = info_div.find_element_by_tag_name("a").get_attribute("href")
        data['name'] = info_div.find_element_by_tag_name("a").text
        data['rapnet_account'] = info_div.find_element_by_class_name("account-details__accountIDText") \
            .text.split(" ")

        data['rapnet_account'] = data['rapnet_account'][len(data['rapnet_account']) - 1]
        details_div = None
        try:
            details_div = table.find_elements_by_class_name("account-details__accountContactSection")
            details_div = details_div[len(details_div) - 1]
        except Exception as err:
            ignore(err)
        details_content_list = details_div.find_elements_by_tag_name("div")
        # parse data
        try:
            for i in range(0, len(details_content_list), 2):
                data[header[details_content_list[i].text]] = details_content_list[i + 1].text
        except Exception as err:
            ignore(err)
        self._seller = Seller(**data)

    def parse_diamond(self):
        data = {
            'price': self._price,
            'seller': self._seller,
        }
        header = {
            "形状": "shape",
            "尺寸": "size",
            "颜色": "color",
            "净度": "clarity",
            "净度 (肉眼干净)": "clarity",
            "切割": "cut",
            "打磨": "polished",
            "对称度": "symmetry",
            "荧光": "fluorescence",
            "深度 %": "depth",
            "台面 %": "table",
            "尺寸": "size_unit",
            "关键特征": "key_features",
            "供应商评论": "supplier_comments",
            "底尖": "bottom_tip",
            "腰": "waist",
            "冠": "crown",
            "亭部": "pavilion",
            "处理的": "processed",
            "题词": "inscription",
            "比率": "ratio",
            "星型刻面长": "star_facet_length",
            "杂质": "impurities",
            "色调": "color_tone",
            "牌子": "brand",
            "报告中的形状": "shape_in_report",
            "报告中的评论": "comments_in_the_report",
        }

        table = self.diamond_element.find_element_by_class_name("table__expandSectionBoxContent--withPadding")
        details_content_title = table.find_elements_by_class_name("flexboxgrid2__col-xs-2")
        details_content_content = table.find_elements_by_class_name("flexboxgrid2__col-xs-4")
        try:
            for i in range(0, len(details_content_title)):
                data[header[details_content_title[i].text]] = details_content_content[i].text \
                    if i + 1 < len(details_content_content) \
                    else table.find_element_by_class_name("flexboxgrid2__col-xs-10").text
        except Exception as err:
            ignore(err)

        self._diamond = Diamond(**data)

    def get_result(self):
        # self._diamond.price = self._price
        # self._diamond.price = self._seller
        return self._diamond


class RapNetRequest:
    def __init__(self):
        self.switch = None
        self.check_model = None

    def func_1_login(self):
        """
        login system
        :return:
        """
        println("login start")
        time.sleep(1)
        email_user_name = document.find_element_by_id("emailUserName")
        password = document.find_element_by_id("password")
        email_user_name.send_keys(NUMBER)
        password.send_keys(PASSWORD)
        btn_login = document.find_element_by_id("btn-login")
        btn_login.click()
        println("login success")

    def func_2_check_menu(self):
        """
        show and save of shapes
        :return:None
        """
        println("wait loading...")
        time.sleep(1)
        menu_item_diamond = wait_element(func=document.find_element_by_name, name="menuItemDiamond")
        try:
            document.find_element_by_class_name("kennylake-close").click()
        except Exception as err:
            ignore(err)
        menu_item_diamond.click()
        # wait search panel
        advanced_panel = wait_element(document.find_element_by_class_name, "advanced-search-form__input-group")
        dmx = wait_element(advanced_panel.find_element_by_class_name, "box-picker-control__more-row")
        dmx.click()
        rap_types = advanced_panel.find_elements_by_class_name("flexboxgrid2__center-xs")
        switch = {}
        for i in rap_types:
            switch[i.text] = i
        # set parameter
        self.switch = switch
        # input models
        select_text = ['{}.{}'.format(i, v) for i, v in enumerate(switch.keys())]
        print("---------------")
        print('\t'.join(select_text))
        # print("请输入选择的形状:", end="")
        BasicAttr.STOP_TEST = True
        # self.check_model = input()
        self.check_model = "0"
        BasicAttr.STOP_TEST = False

    def func_3_select_click_model(self):
        """
        checkout shape and search contents
        :return:
        """
        # Because the creation of the hash table has been sorted
        # there is no need to worry about disorder
        keys = list(self.switch.keys())
        checks = [self.switch[keys[int(i)]] for i in self.check_model]
        for i in checks:
            i.click()
        search_btn = wait_element(document.find_elements_by_class_name, 'search__footerButton', 0)
        search_btn.click()

    def func_4_get_table(self):
        # Two-dimensional array
        header_row = wait_element(document.find_element_by_id, "searchResultTable-headerRow")
        table_head = ['未知字段']
        table_head.extend(str(header_row.text).split("\n"))
        # data body
        # table_body = wait_element(document.find_element_by_id, "searchResultTable-tableBody")
        # Wait loading data list
        f, total, next_btn = get_footer()
        while next_btn:
            println("Start the thread for each page...")
            table_body = wait_element(document.find_element_by_id, "searchResultTable-tableBody")
            rows = wait_element(table_body.find_elements_by_class_name, "tableRowWrapper")
            for down in rows:
                println(str(down))
                while 1:
                    try:
                        down.click()
                    except:
                        time.sleep(1)
                        continue
                    else:
                        break
                # 取第二个div详细信息
                div = wait_element(down.find_element_by_class_name, "searchResultTableExpandedView")
                target = div.find_element_by_class_name("table__expandContainer")
                # 钻石细节
                price_div = diamond_div = seller_div = None
                try:
                    diamond_div = target.find_element_by_class_name("diamondDetails")
                except Exception as err:
                    ignore(err)
                # diamond_div = wait_element(target.find_element_by_class_name, "diamondDetails")
                # 价格
                try:
                    price_div = target.find_element_by_class_name("additionalDetails")
                except Exception as err:
                    ignore(err)
                # price_div = wait_element(target.find_element_by_class_name, "additionalDetails")

                # 卖家信息
                try:
                    seller_div = target.find_element_by_class_name("sellerDetailsZero")
                except Exception as err:
                    try:
                        seller_div = target.find_element_by_class_name("sellerDetails")
                    except Exception as err_err:
                        ignore(err_err)
                    ignore(err)
                # seller_div = wait_element(target.find_element_by_class_name, "sellerDetailsZero")
                # 获得解析工具
                parse = ParseUtil(diamond_div, price_div, seller_div)

                parse.parse_price()
                parse.parse_seller()
                parse.parse_diamond()
                # get a single data
                diamond = parse.get_result()
                # save data
                BasicAttr.session.add(diamond)
                BasicAttr.session.commit()
                println("A piece of data has been inserted")
                time.sleep(0.5)
            next_btn.click()
            time.sleep(2)
            f, total, next_btn = get_footer()
        # self.table_head = table_head
        # self.table_data = table_data


def check(model):
    seller_count = BasicAttr.session.query(sqlalchemy.func.count(distinct(model.id))).scalar()
    if seller_count != 0:
        BasicAttr.STOP_TEST = True
        while 1:
            print(f"do you want to clear old data [{str(model)}]?(y/n):")
            i = input()
            if i == 'y':
                BasicAttr.session.query(model).delete()
                break
            elif i == 'n':
                break
            else:
                print("input error,Please enter again")
        BasicAttr.STOP_TEST = False


def start():
    """Start main work"""
    # check table
    # check(Diamond)
    # check(Seller)
    # check(Price)
    try:
        rnr = RapNetRequest()
        methods = get_static_fields(dir(rnr), BasicAttr.BASE_ATTR)
        methods = sorted(methods)
        document.get(URL)
        for i in methods:
            # Prevent empty
            attr = getattr(rnr, str(i))
            if callable(attr):
                attr()
    except Exception as err:
        println(str(err), color=BasicAttr.LOG_COLOR['R'])
    println("Exit System.")
    document.quit()
    BasicAttr.EXIT_STATUS = True


if __name__ == '__main__':
    # Use thread model
    println("Start system,Please wait....")
    t = threading.Thread(target=start, name='start')
    # verification thread
    e = threading.Thread(target=error_detection, name='err')
    t.start()
    e.start()
    t.join()
    e.join()
